// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package modfetch

import (
	"archive/zip"
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/fs"
	"os"
	"path"
	"sort"
	"strings"
	"time"

	"cmd/go/internal/modfetch/codehost"

	"golang.org/x/mod/modfile"
	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
	modzip "golang.org/x/mod/zip"
)

// codeRepo使用底层的codehost.Repo实现modfetch.Repo。
type codeRepo struct {
	modPath string

	// 代码是包含此模块的存储库。
	code codehost.Repo
	// codeRoot是代码根处的导入路径。
	codeRoot string
	// codeDir是我们希望在其中找到模块的目录（相对于根目录）。
	// 如果pathMajor为非空且codeRoot不是完整modPath，
	// 然后我们查看codeDir和codeDir/pathMajor[1:]。
	codeDir string

	// pathMajor是modPath的后缀，表示其主要版本，
	// 或者如果modPath的主版本为0或1，则为空字符串。
	// None
	// pathMajor的格式通常为“/vN”，但也可能为“.vN”，或
	// 使用gopkg.in解析的模块的“.vN不稳定”。
	pathMajor string
	// pathPrefix是排除pathMajor的modPath的前缀。
	// 它仅用于日志记录。
	pathPrefix string

	// pseudoMajor是生成时需要的主要版本前缀
	// 此模块的伪版本，从模块路径派生。伪主流
	// 如果模块路径不包含版本后缀（即，
	// 接受v0或v1）。
	pseudoMajor string
}

// newCodeRepo返回一个Repo，该Repo读取具有
// 给定路径，来自存储在代码中的repo，具有repo的根
// 包含codeRoot给定的路径。
func newCodeRepo(code codehost.Repo, codeRoot, path string) (Repo, error) {
	if !hasPathPrefix(path, codeRoot) {
		return nil, fmt.Errorf("mismatched repo: found %s for %s", codeRoot, path)
	}
	pathPrefix, pathMajor, ok := module.SplitPathVersion(path)
	if !ok {
		return nil, fmt.Errorf("invalid module path %q", path)
	}
	if codeRoot == path {
		pathPrefix = path
	}
	pseudoMajor := module.PathMajorPrefix(pathMajor)

	// Compute codeDir=bar，repo中的子目录
	// 对应于模块根目录。
	// None
	// 在这一点上，我们可能会：
	// path=github.com/rsc/foo/bar/v2
	// codeRoot=github.com/rsc/foo
	// pathPrefix=github.com/rsc/foo/bar
	// pathMajor=/v2
	// 伪大分子=v2
	// None
	// 给
	// codeDir=条
	// None
	// 我们知道pathPrefix是path的前缀，codeRoot是path的前缀
	// 路径，但codeRoot可能是也可能不是pathPrefix的前缀，因为
	// codeRoot可能是整个路径（在这种情况下，codeDir应该为空）。
	// 这在两种情况下发生。
	// None
	// 一个是当go import meta标记解析完整的模块路径时，
	// 包括pathMajor后缀：
	// path=nanomsg.org/go/mangos/v2
	// codeRoot=nanomsg.org/go/mangos/v2
	// pathPrefix=nanomsg.org/go/mangos
	// pathMajor=/v2
	// 伪大分子=v2
	// None
	// 另一个类似：仅对于gopkg.in，主要版本是编码的
	// 使用点而不是斜杠，因此不能位于子目录中。
	// 路径=gopkg.in/yaml.v2
	// codeRoot=gopkg.in/yaml.v2
	// 路径前缀=gopkg.in/yaml
	// pathMajor=.v2
	// 伪大分子=v2
	// None
	codeDir := ""
	if codeRoot != path {
		if !hasPathPrefix(pathPrefix, codeRoot) {
			return nil, fmt.Errorf("repository rooted at %s cannot contain module %s", codeRoot, path)
		}
		codeDir = strings.Trim(pathPrefix[len(codeRoot):], "/")
	}

	r := &codeRepo{
		modPath:     path,
		code:        code,
		codeRoot:    codeRoot,
		codeDir:     codeDir,
		pathPrefix:  pathPrefix,
		pathMajor:   pathMajor,
		pseudoMajor: pseudoMajor,
	}

	return r, nil
}

func (r *codeRepo) ModulePath() string {
	return r.modPath
}

func (r *codeRepo) Versions(prefix string) ([]string, error) {
	// 特殊情况：gopkg.in/macaroon-bakery.v2-unstable
	// 不使用v2标签（用于macaroon bakery.v2）。
	// 它根本没有可能的标签。
	if strings.HasPrefix(r.modPath, "gopkg.in/") && strings.HasSuffix(r.modPath, "-unstable") {
		return nil, nil
	}

	p := prefix
	if r.codeDir != "" {
		p = r.codeDir + "/" + p
	}
	tags, err := r.code.Tags(p)
	if err != nil {
		return nil, &module.ModuleError{
			Path: r.modPath,
			Err:  err,
		}
	}

	var list, incompatible []string
	for _, tag := range tags {
		if !strings.HasPrefix(tag, p) {
			continue
		}
		v := tag
		if r.codeDir != "" {
			v = v[len(r.codeDir)+1:]
		}
		if v == "" || v != module.CanonicalVersion(v) || module.IsPseudoVersion(v) {
			continue
		}

		if err := module.CheckPathMajor(v, r.pathMajor); err != nil {
			if r.codeDir == "" && r.pathMajor == "" && semver.Major(v) > "v1" {
				incompatible = append(incompatible, v)
			}
			continue
		}

		list = append(list, v)
	}
	semver.Sort(list)
	semver.Sort(incompatible)

	return r.appendIncompatibleVersions(list, incompatible)
}

// AppendCompatibileLeverons在列表中添加“+不兼容”版本，如果
// 如果合适，返回最终列表。
// None
// 不兼容列表包含没有“+不兼容”的候选版本
// 前缀
// None
// 列表和不兼容都必须按语义顺序排序。
func (r *codeRepo) appendIncompatibleVersions(list, incompatible []string) ([]string, error) {
	if len(incompatible) == 0 || r.pathMajor != "" {
		// 不可能有+不兼容的版本，因此无需检查它们。
		return list, nil
	}

	versionHasGoMod := func(v string) (bool, error) {
		_, err := r.code.ReadFile(v, "go.mod", codehost.MaxGoMod)
		if err == nil {
			return true, nil
		}
		if !os.IsNotExist(err) {
			return false, &module.ModuleError{
				Path: r.modPath,
				Err:  err,
			}
		}
		return false, nil
	}

	if len(list) > 0 {
		ok, err := versionHasGoMod(list[len(list)-1])
		if err != nil {
			return nil, err
		}
		if ok {
			// 最新的兼容版本有一个go.mod文件，因此假设所有
			// 后续版本也会这样做，并且不包括任何+不兼容
			// 版本。即使我们是错的，作者显然是有意的
			// 用电设备应位于v0/v1线上，而不是更高的+不兼容线路
			// 版本（见https:
			// None
			// 我们知道至少有两个例子需要这种行为
			// （github.com/russross）/blackfriday@v2.0.0和
			// github.com/libp2p/go-libp2p@v6.0.23)，和（截至2019年10月29日）没有
			// 具体的例子，这是不希望的。
			return list, nil
		}
	}

	var (
		lastMajor         string
		lastMajorHasGoMod bool
	)
	for i, v := range incompatible {
		major := semver.Major(v)

		if major != lastMajor {
			rem := incompatible[i:]
			j := sort.Search(len(rem), func(j int) bool {
				return semver.Major(rem[j]) != major
			})
			latestAtMajor := rem[j-1]

			var err error
			lastMajor = major
			lastMajorHasGoMod, err = versionHasGoMod(latestAtMajor)
			if err != nil {
				return nil, err
			}
		}

		if lastMajorHasGoMod {
			// 这个主要版本的最新版本有一个go.mod文件，所以它是
			// 不允许为+不兼容。如果包括一些
			// 此主版本的次版本as+不兼容，但需要
			// 其他人的语义导入版本控制，因此删除所有+不兼容
			// 此主要版本的版本。
			// None
			// 如果我们在中间使用了一个小版本，那么用户仍然是错误的。
			// 能够显式地“获取”该版本的特定标记-它们只是
			// 不会出现在“go list”中，也不会作为不平等查询的结果出现
			// 界限。
			continue
		}
		list = append(list, v+"+incompatible")
	}

	return list, nil
}

func (r *codeRepo) Stat(rev string) (*RevInfo, error) {
	if rev == "latest" {
		return r.Latest()
	}
	codeRev := r.revToRev(rev)
	info, err := r.code.Stat(codeRev)
	if err != nil {
		return nil, &module.ModuleError{
			Path: r.modPath,
			Err: &module.InvalidVersionError{
				Version: rev,
				Err:     err,
			},
		}
	}
	return r.convert(info, rev)
}

func (r *codeRepo) Latest() (*RevInfo, error) {
	info, err := r.code.Latest()
	if err != nil {
		return nil, err
	}
	return r.convert(info, "")
}

// convert将代码宿主报告的版本转换为
// 由模块系统进行解释。
// None
// 如果statVers是有效的模块版本，则它将用于版本字段。
// 否则，版本将从传入的信息和最近的标记派生。
func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) {
	info2 := &RevInfo{
		Name:  info.Name,
		Short: info.Short,
		Time:  info.Time,
	}

	// 如果这是普通标记（无目录/前缀）
	// 并且模块路径未设置版本，
	// 如果基础文件树没有go.mod，
	// 然后允许使用带有+不兼容后缀的标记。
	var canUseIncompatible func() bool
	canUseIncompatible = func() bool {
		var ok bool
		if r.codeDir == "" && r.pathMajor == "" {
			_, errGoMod := r.code.ReadFile(info.Name, "go.mod", codehost.MaxGoMod)
			if errGoMod != nil {
				ok = true
			}
		}
		canUseIncompatible = func() bool { return ok }
		return ok
	}

	invalidf := func(format string, args ...interface{}) error {
		return &module.ModuleError{
			Path: r.modPath,
			Err: &module.InvalidVersionError{
				Version: info2.Version,
				Err:     fmt.Errorf(format, args...),
			},
		}
	}

	// checkGoMod验证模块的go.mod文件是否存在
	// 根据info2.Version和r表示的模块路径的要求存在。
	checkGoMod := func() (*RevInfo, error) {
		// 如果r.codeDir为非空，则go.mod文件必须存在：模块
		// 作者——而不是模块消费者——决定如何分割回购协议
		// 分为模块。
		// None
		// 相反，如果go.mod文件存在，则模块作者—而不是模块
		// 使用者-用于确定模块的路径
		// None
		// r、 findDir验证这两种情况。现在执行它，以便
		// r、 如果go.mod位置或
		// 声明的模块路径不匹配。
		_, _, _, err := r.findDir(info2.Version)
		if err != nil {
			// TODO:如果返回一个错误，比如“不是模块”，那就太好了。
			// 现在我们返回“missing go.mod”，这有点让人困惑。
			return nil, &module.ModuleError{
				Path: r.modPath,
				Err: &module.InvalidVersionError{
					Version: info2.Version,
					Err:     notExistError{err: err},
				},
			}
		}

		// 如果版本+不兼容，则go.mod文件不得存在：
		// +“不兼容”不是正在从语义导入版本控制中选择退出。
		if strings.HasSuffix(info2.Version, "+incompatible") {
			if !canUseIncompatible() {
				if r.pathMajor != "" {
					return nil, invalidf("+incompatible suffix not allowed: module path includes a major version suffix, so major version must match")
				} else {
					return nil, invalidf("+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required")
				}
			}

			if err := module.CheckPathMajor(strings.TrimSuffix(info2.Version, "+incompatible"), r.pathMajor); err == nil {
				return nil, invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(info2.Version))
			}
		}

		return info2, nil
	}

	// 确定版本。
	// None
	// 如果statVers是规范的，那么最初的调用是repo.Stat（statVers）。
	// 因为这个版本是规范的，所以我们不能将它解析为除
	// 本身，可能带有“+不兼容”注释：我们不需要这样做
	// 查找任意伪版本所需的工作。
	if statVers != "" && statVers == module.CanonicalVersion(statVers) {
		info2.Version = statVers

		if module.IsPseudoVersion(info2.Version) {
			if err := r.validatePseudoVersion(info, info2.Version); err != nil {
				return nil, err
			}
			return checkGoMod()
		}

		if err := module.CheckPathMajor(info2.Version, r.pathMajor); err != nil {
			if canUseIncompatible() {
				info2.Version += "+incompatible"
				return checkGoMod()
			} else {
				if vErr, ok := err.(*module.InvalidVersionError); ok {
					// 我们将更详细地描述该版本无效的原因，
					// 所以去掉现有的“无效版本”包装。
					err = vErr.Err
				}
				return nil, invalidf("module contains a go.mod file, so major version must be compatible: %v", err)
			}
		}

		return checkGoMod()
	}

	// statVers是空的或非规范的，所以我们需要将其解析为规范的
	// 版本或伪版本。

	// 从代码repo标记派生或验证版本。
	// 标记必须具有与codeDir匹配的前缀。
	tagPrefix := ""
	if r.codeDir != "" {
		tagPrefix = r.codeDir + "/"
	}

	isRetracted, err := r.retractedVersions()
	if err != nil {
		isRetracted = func(string) bool { return false }
	}

	// tagToVersion返回从标记修剪tagPrefix获得的版本。
	// 如果标记无效、已收回或为伪版本，则tagToVersion返回
	// 空版本。
	tagToVersion := func(tag string) (v string, tagIsCanonical bool) {
		if !strings.HasPrefix(tag, tagPrefix) {
			return "", false
		}
		trimmed := tag[len(tagPrefix):]
		// 看起来像伪版本的标记会让人困惑。别理他们。
		if module.IsPseudoVersion(tag) {
			return "", false
		}

		v = semver.Canonical(trimmed) // 不是module.Canonical：我们不想从标记中获取显式的“+不兼容”后缀。
		if v == "" || !strings.HasPrefix(trimmed, v) {
			return "", false // 无效或不完整的版本（仅vX或vX.Y）。
		}
		if isRetracted(v) {
			return "", false
		}
		if v == trimmed {
			tagIsCanonical = true
		}

		if err := module.CheckPathMajor(v, r.pathMajor); err != nil {
			if canUseIncompatible() {
				return v + "+incompatible", tagIsCanonical
			}
			return "", false
		}

		return v, tagIsCanonical
	}

	// 如果VCS给了我们一个有效的版本，就使用它。
	if v, tagIsCanonical := tagToVersion(info.Version); tagIsCanonical {
		info2.Version = v
		return checkGoMod()
	}

	// 查看修订版上的标记，查找可用的规范版本
	// 或者为伪版本提供适当的基础。
	var pseudoBase string
	for _, pathTag := range info.Tags {
		v, tagIsCanonical := tagToVersion(pathTag)
		if tagIsCanonical {
			if statVers != "" && semver.Compare(v, statVers) == 0 {
				// 用户请求了非规范版本，但
				// 规范等价物指的是同一版本。使用它。
				info2.Version = v
				return checkGoMod()
			} else {
				// 保存修订版的最高规范标记。如果我们找不到
				// 更好的匹配，我们将使用它作为规范版本。
				// None
				// 注意：不要将其替换为semver.Max。尽管有名字，
				// Max*还*规范化其参数，使用
				// 而不是module.CanonicalVersion，因此
				// 去掉我们的“+不兼容”后缀。
				if semver.Compare(info2.Version, v) < 0 {
					info2.Version = v
				}
			}
		} else if v != "" && semver.Compare(v, statVers) == 0 {
			// 用户明确请求了与此标记等效的内容。我们
			// 无法直接使用标记中的版本：因为标记不是
			// 根据规范，它可能是模棱两可的。例如，标签v0.0.1+a和
			// v0.0.1+b可能同时存在，并参考不同版本。
			// None
			// 该标记对于模块是有效的，因此我们至少可以将其用作
			// 明确的伪版本的基础。
			// None
			// 如果多个标记匹配，tagToVersion会将它们规范化为同一个标记
			// 基本版本。
			pseudoBase = v
		}
	}

	// 如果我们找到了修订版的任何规范标记，请返回它。
	// 即使我们找到了一个好的伪版本库，规范版本也更好。
	if info2.Version != "" {
		return checkGoMod()
	}

	// 查找修订历史记录中标记最高的版本，具体取决于
	// 主要版本和+不兼容的约束。使用该版本作为
	// 伪版本库，使伪版本排序更高。忽视
	// 收回版本。
	allowedMajor := func(major string) func(v string) bool {
		return func(v string) bool {
			return (major == "" || semver.Major(v) == major) && !isRetracted(v)
		}
	}
	if pseudoBase == "" {
		var tag string
		if r.pseudoMajor != "" || canUseIncompatible() {
			tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor(r.pseudoMajor))
		} else {
			// 允许v1或v0，但不能兼容更高版本。
			tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v1"))
			if tag == "" {
				tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v0"))
			}
		}
		pseudoBase, _ = tagToVersion(tag) // 如果标记无效，则为空
	}

	info2.Version = module.PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short)
	return checkGoMod()
}

// ValidatePSeudVersion检查版本是否有与兼容的主版本
// r、 modPath并对基本版本进行编码，并提交符合
// 信息。
// None
// 请注意，特别是验证非平凡的基本版本可能有点困难
// 昂贵：为此，r.code.DensesFrom需要至少获取
// 足够多的提交历史记录，可以找到版本与其基础之间的路径。
// 幸运的是，有许多伪版本，比如那些未标记的存储库-
// 有琐碎的基础！
func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string) (err error) {
	defer func() {
		if err != nil {
			if _, ok := err.(*module.ModuleError); !ok {
				if _, ok := err.(*module.InvalidVersionError); !ok {
					err = &module.InvalidVersionError{Version: version, Pseudo: true, Err: err}
				}
				err = &module.ModuleError{Path: r.modPath, Err: err}
			}
		}
	}()

	if err := module.CheckPathMajor(version, r.pathMajor); err != nil {
		return err
	}

	rev, err := module.PseudoVersionRev(version)
	if err != nil {
		return err
	}
	if rev != info.Short {
		switch {
		case strings.HasPrefix(rev, info.Short):
			return fmt.Errorf("revision is longer than canonical (%s)", info.Short)
		case strings.HasPrefix(info.Short, rev):
			return fmt.Errorf("revision is shorter than canonical (%s)", info.Short)
		default:
			return fmt.Errorf("does not match short name of revision (%s)", info.Short)
		}
	}

	t, err := module.PseudoVersionTime(version)
	if err != nil {
		return err
	}
	if !t.Equal(info.Time.Truncate(time.Second)) {
		return fmt.Errorf("does not match version-control timestamp (expected %s)", info.Time.UTC().Format(module.PseudoVersionTimestampFormat))
	}

	tagPrefix := ""
	if r.codeDir != "" {
		tagPrefix = r.codeDir + "/"
	}

	// 伪版本的优先级应刚好高于其父版本，
	// 没有比这更高的了。否则，图书馆作者可能会“锁定”
	// 依赖项版本（并绕过通常的最低版本选择）
	// 命名一个非常高的伪版本，而不是一个精确的版本。
	// None
	// 此外，如果我们允许伪版本使用任意预发布
	// 标记，则每个提交都有无限多个可能的名称。每个
	// name消耗模块缓存和代理中的资源，因此我们希望
	// 在模块作者的控制下，将它们限制为有限集。
	// None
	// 我们通过要求
	// 伪版本是基于引用某个版本的祖先。我们
	// 在构造新的伪版本时更喜欢这样的最高标记，但不要这样做
	// 在解析现有的伪版本时不强制执行该属性：我们不执行
	// 知道何时添加父标记，最高标记的父标记可能不会
	// 在首次解析伪版本时已存在。
	base, err := module.PseudoVersionBase(strings.TrimSuffix(version, "+incompatible"))
	if err != nil {
		return err
	}
	if base == "" {
		if r.pseudoMajor == "" && semver.Major(version) == "v1" {
			return fmt.Errorf("major version without preceding tag must be v0, not v1")
		}
		return nil
	} else {
		for _, tag := range info.Tags {
			versionOnly := strings.TrimPrefix(tag, tagPrefix)
			if versionOnly == base {
				// 基本版本是规范的，因此如果标签中的版本是
				// 字面上是相等的（不仅仅是等价的），那么标签也是规范的。
				// None
				// 我们允许从服务器上的非规范标记派生伪版本
				// 同样的提交，所以像“v1.1.0+一些元数据”这样的标记可以解析为
				// 尽可能接近规范版本（“v1.1.0”），同时
				// 强制执行总排序（“v1.1.1-0.[…]”，带有唯一后缀）。
				// None
				// 然而，规范化标记已经有了一个总的顺序，因此没有
				// 不直接使用规范标记的原因，我们知道
				// 规范标记必须已存在，因为伪版本为
				// 从中衍生出来的。在这种情况下，请参考
				// 从其自身的规范标记派生的伪版本令人困惑。
				return fmt.Errorf("tag (%s) found on revision %s is already canonical, so should not be replaced with a pseudo-version derived from that tag", tag, rev)
			}
		}
	}

	tags, err := r.code.Tags(tagPrefix + base)
	if err != nil {
		return err
	}

	var lastTag string // 更喜欢记录一些真实的标记，而不是规范上等价的基。
	ancestorFound := false
	for _, tag := range tags {
		versionOnly := strings.TrimPrefix(tag, tagPrefix)
		if semver.Compare(versionOnly, base) == 0 {
			lastTag = tag
			ancestorFound, err = r.code.DescendsFrom(info.Name, tag)
			if ancestorFound {
				break
			}
		}
	}

	if lastTag == "" {
		return fmt.Errorf("preceding tag (%s) not found", base)
	}

	if !ancestorFound {
		if err != nil {
			return err
		}
		rev, err := module.PseudoVersionRev(version)
		if err != nil {
			return fmt.Errorf("not a descendent of preceding tag (%s)", lastTag)
		}
		return fmt.Errorf("revision %s is not a descendent of preceding tag (%s)", rev, lastTag)
	}
	return nil
}

func (r *codeRepo) revToRev(rev string) string {
	if semver.IsValid(rev) {
		if module.IsPseudoVersion(rev) {
			r, _ := module.PseudoVersionRev(rev)
			return r
		}
		if semver.Build(rev) == "+incompatible" {
			rev = rev[:len(rev)-len("+incompatible")]
		}
		if r.codeDir == "" {
			return rev
		}
		return r.codeDir + "/" + rev
	}
	return rev
}

func (r *codeRepo) versionToRev(version string) (rev string, err error) {
	if !semver.IsValid(version) {
		return "", &module.ModuleError{
			Path: r.modPath,
			Err: &module.InvalidVersionError{
				Version: version,
				Err:     errors.New("syntax error"),
			},
		}
	}
	return r.revToRev(version), nil
}

// findDir在包含模块的repo中查找目录。
// None
// 如果r.pathMajor为非空，则可以是r.codeDir或-如果是go.mod
// 文件存在-r.codeDir/r.pathmark[1:]。
func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err error) {
	rev, err = r.versionToRev(version)
	if err != nil {
		return "", "", nil, err
	}

	// 加载关于go.mod的信息，但延迟考虑
	// （除了I/O错误）直到我们排除v2/go.mod。
	file1 := path.Join(r.codeDir, "go.mod")
	gomod1, err1 := r.code.ReadFile(rev, file1, codehost.MaxGoMod)
	if err1 != nil && !os.IsNotExist(err1) {
		return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file1, rev, err1)
	}
	mpath1 := modfile.ModulePath(gomod1)
	found1 := err1 == nil && (isMajor(mpath1, r.pathMajor) || r.canReplaceMismatchedVersionDueToBug(mpath1))

	var file2 string
	if r.pathMajor != "" && r.codeRoot != r.modPath && !strings.HasPrefix(r.pathMajor, ".") {
		// 假设pathMajor是“/v2”。
		// go.mod应声明v2，v2/go.mod不应存在，
		// 或者v2/go.mod应该存在并声明v2。不是两者都有。
		// 注意，我们不检查完整路径，只检查主后缀，
		// 因为更换了模块。这可能是一个叉子
		// 真正的模块，位于不同的路径，仅在
		// 替换指令。
		dir2 := path.Join(r.codeDir, r.pathMajor[1:])
		file2 = path.Join(dir2, "go.mod")
		gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod)
		if err2 != nil && !os.IsNotExist(err2) {
			return "", "", nil, fmt.Errorf("reading %s/%s at revision %s: %v", r.pathPrefix, file2, rev, err2)
		}
		mpath2 := modfile.ModulePath(gomod2)
		found2 := err2 == nil && isMajor(mpath2, r.pathMajor)

		if found1 && found2 {
			return "", "", nil, fmt.Errorf("%s/%s and ...%s/go.mod both have ...%s module paths at revision %s", r.pathPrefix, file1, r.pathMajor, r.pathMajor, rev)
		}
		if found2 {
			return rev, dir2, gomod2, nil
		}
		if err2 == nil {
			if mpath2 == "" {
				return "", "", nil, fmt.Errorf("%s/%s is missing module path at revision %s", r.pathPrefix, file2, rev)
			}
			return "", "", nil, fmt.Errorf("%s/%s has non-...%s module path %q at revision %s", r.pathPrefix, file2, r.pathMajor, mpath2, rev)
		}
	}

	// 不是v2/go.mod，所以要么是go.mod，要么什么都没有。是哪一个？
	if found1 {
		// 显式go.mod与匹配的主版本ok。
		return rev, r.codeDir, gomod1, nil
	}
	if err1 == nil {
		// 不允许使用不匹配主版本的显式go.mod。
		suffix := ""
		if file2 != "" {
			suffix = fmt.Sprintf(" (and ...%s/go.mod does not exist)", r.pathMajor)
		}
		if mpath1 == "" {
			return "", "", nil, fmt.Errorf("%s is missing module path%s at revision %s", file1, suffix, rev)
		}
		if r.pathMajor != "" { // 用于gopkg.in的“.v1”和“.v2”
			return "", "", nil, fmt.Errorf("%s has non-...%s module path %q%s at revision %s", file1, r.pathMajor, mpath1, suffix, rev)
		}
		if _, _, ok := module.SplitPathVersion(mpath1); !ok {
			return "", "", nil, fmt.Errorf("%s has malformed module path %q%s at revision %s", file1, mpath1, suffix, rev)
		}
		return "", "", nil, fmt.Errorf("%s has post-%s module path %q%s at revision %s", file1, semver.Major(version), mpath1, suffix, rev)
	}

	if r.codeDir == "" && (r.pathMajor == "" || strings.HasPrefix(r.pathMajor, ".")) {
		// 对于v0/v1和gopkg.in，回购根处的隐式go.mod正常。
		return rev, "", nil, nil
	}

	// 不允许在回购根下方或v2+处隐式go.mod。
	// 明确v2+使用任一位置的可能性。
	if file2 != "" {
		return "", "", nil, fmt.Errorf("missing %s/go.mod and ...%s/go.mod at revision %s", r.pathPrefix, r.pathMajor, rev)
	}
	return "", "", nil, fmt.Errorf("missing %s/go.mod at revision %s", r.pathPrefix, rev)
}

// isMajor报告mpath允许的版本是否与兼容
// pathMajor暗示的主版本，如果mpath具有无效的
// 版本后缀。
func isMajor(mpath, pathMajor string) bool {
	if mpath == "" {
		// 如果没有路径，我们就不知道它与哪个版本兼容。
		return false
	}
	_, mpathMajor, ok := module.SplitPathVersion(mpath)
	if !ok {
		// 无效的模块路径与任何版本都不兼容。
		return false
	}
	if pathMajor == "" {
		// gopkg.in模块的所有有效版本都需要
		// 版本v0或v1与空
		// 少校。
		switch module.PathMajorPrefix(mpathMajor) {
		case "", "v0", "v1":
			return true
		default:
			return false
		}
	}
	if mpathMajor == "" {
		// 即使pathMajor是“.v0”或“.v1”，我们也不能确定模块
		// 如果没有后缀，则会进行适当的标记。此外，我们不希望克隆人
		// 非gopkg.in模块的路径为gopkg.in，因此，
		// mpath中的non-gopkg.in可能是任何此类pathMajor的错误模块
		// 无论如何
		return false
	}
	// 如果pathMajor和mpathMajor都是非空的，那么我们只关心它们
	// 具有相同的主版本验证规则。通过A/v2获取的克隆
	// path可能会将模块替换为路径gopkg.in/foo.v2-unstable，这就是
	// 好啊
	return pathMajor[1:] == mpathMajor[1:]
}

// CanReplaceMatchedVersionDueTobug报告r的版本
// 可以用其他不匹配的主版本替换mpath的版本
// 由于Go命令（golang.org/issue/34254）中的历史错误。
func (r *codeRepo) canReplaceMismatchedVersionDueToBug(mpath string) bool {
	// 错误导致我们错误地接受未版本化的路径作为替换
	// 对于版本化的gopkg.in路径。
	unversioned := r.pathMajor == ""
	replacingGopkgIn := strings.HasPrefix(mpath, "gopkg.in/")
	return unversioned && replacingGopkgIn
}

func (r *codeRepo) GoMod(version string) (data []byte, err error) {
	if version != module.CanonicalVersion(version) {
		return nil, fmt.Errorf("version %s is not canonical", version)
	}

	if module.IsPseudoVersion(version) {
		// findDir忽略伪版本中编码的元数据，
		// 仅使用结尾处的修订。
		// 调用Stat显式验证元数据，这样我们就不会返回
		// 无效版本的伪造文件。
		_, err := r.Stat(version)
		if err != nil {
			return nil, err
		}
	}

	rev, dir, gomod, err := r.findDir(version)
	if err != nil {
		return nil, err
	}
	if gomod != nil {
		return gomod, nil
	}
	data, err = r.code.ReadFile(rev, path.Join(dir, "go.mod"), codehost.MaxGoMod)
	if err != nil {
		if os.IsNotExist(err) {
			return LegacyGoMod(r.modPath), nil
		}
		return nil, err
	}
	return data, nil
}

// LegacyGoMod为没有go.mod文件的模块生成一个伪go.mod文件。
// go.mod文件包含一个模块指令，没有其他内容：no go version，
// 没有要求。
// None
// 我们曾经尝试构建一个go.mod来反映现有的
// 包管理元数据文件，但转换
// 本质上是不完美的（因为这些文件没有
// 与go.mod的语义完全相同）和
// 对于构建中的依赖关系，不可能
// 对的于是我们停了下来。
func LegacyGoMod(modPath string) []byte {
	return []byte(fmt.Sprintf("module %s\n", modfile.AutoQuote(modPath)))
}

func (r *codeRepo) modPrefix(rev string) string {
	return r.modPath + "@" + rev
}

func (r *codeRepo) retractedVersions() (func(string) bool, error) {
	versions, err := r.Versions("")
	if err != nil {
		return nil, err
	}

	for i, v := range versions {
		if strings.HasSuffix(v, "+incompatible") {
			versions = versions[:i]
			break
		}
	}
	if len(versions) == 0 {
		return func(string) bool { return false }, nil
	}

	var highest string
	for i := len(versions) - 1; i >= 0; i-- {
		v := versions[i]
		if semver.Prerelease(v) == "" {
			highest = v
			break
		}
	}
	if highest == "" {
		highest = versions[len(versions)-1]
	}

	data, err := r.GoMod(highest)
	if err != nil {
		return nil, err
	}
	f, err := modfile.ParseLax("go.mod", data, nil)
	if err != nil {
		return nil, err
	}
	retractions := make([]modfile.VersionInterval, len(f.Retract))
	for _, r := range f.Retract {
		retractions = append(retractions, r.VersionInterval)
	}

	return func(v string) bool {
		for _, r := range retractions {
			if semver.Compare(r.Low, v) <= 0 && semver.Compare(v, r.High) <= 0 {
				return true
			}
		}
		return false
	}, nil
}

func (r *codeRepo) Zip(dst io.Writer, version string) error {
	if version != module.CanonicalVersion(version) {
		return fmt.Errorf("version %s is not canonical", version)
	}

	if module.IsPseudoVersion(version) {
		// findDir忽略伪版本中编码的元数据，
		// 仅使用结尾处的修订。
		// 调用Stat显式验证元数据，这样我们就不会返回
		// 无效版本的伪造文件。
		_, err := r.Stat(version)
		if err != nil {
			return err
		}
	}

	rev, subdir, _, err := r.findDir(version)
	if err != nil {
		return err
	}
	dl, err := r.code.ReadZip(rev, subdir, codehost.MaxZipFile)
	if err != nil {
		return err
	}
	defer dl.Close()
	subdir = strings.Trim(subdir, "/")

	// 假脱机到本地文件。
	f, err := os.CreateTemp("", "go-codehost-")
	if err != nil {
		dl.Close()
		return err
	}
	defer os.Remove(f.Name())
	defer f.Close()
	maxSize := int64(codehost.MaxZipFile)
	lr := &io.LimitedReader{R: dl, N: maxSize + 1}
	if _, err := io.Copy(f, lr); err != nil {
		dl.Close()
		return err
	}
	dl.Close()
	if lr.N <= 0 {
		return fmt.Errorf("downloaded zip file too large")
	}
	size := (maxSize + 1) - lr.N
	if _, err := f.Seek(0, 0); err != nil {
		return err
	}

	// 从zip文件转换为我们想要的zip文件。
	zr, err := zip.NewReader(f, size)
	if err != nil {
		return err
	}

	var files []modzip.File
	if subdir != "" {
		subdir += "/"
	}
	haveLICENSE := false
	topPrefix := ""
	for _, zf := range zr.File {
		if topPrefix == "" {
			i := strings.Index(zf.Name, "/")
			if i < 0 {
				return fmt.Errorf("missing top-level directory prefix")
			}
			topPrefix = zf.Name[:i+1]
		}
		if !strings.HasPrefix(zf.Name, topPrefix) {
			return fmt.Errorf("zip file contains more than one top-level directory")
		}
		name := strings.TrimPrefix(zf.Name, topPrefix)
		if !strings.HasPrefix(name, subdir) {
			continue
		}
		name = strings.TrimPrefix(name, subdir)
		if name == "" || strings.HasSuffix(name, "/") {
			continue
		}
		files = append(files, zipFile{name: name, f: zf})
		if name == "LICENSE" {
			haveLICENSE = true
		}
	}

	if !haveLICENSE && subdir != "" {
		data, err := r.code.ReadFile(rev, "LICENSE", codehost.MaxLICENSE)
		if err == nil {
			files = append(files, dataFile{name: "LICENSE", data: data})
		}
	}

	return modzip.Create(dst, module.Version{Path: r.modPath, Version: version}, files)
}

type zipFile struct {
	name string
	f    *zip.File
}

func (f zipFile) Path() string                 { return f.name }
func (f zipFile) Lstat() (fs.FileInfo, error)  { return f.f.FileInfo(), nil }
func (f zipFile) Open() (io.ReadCloser, error) { return f.f.Open() }

type dataFile struct {
	name string
	data []byte
}

func (f dataFile) Path() string                { return f.name }
func (f dataFile) Lstat() (fs.FileInfo, error) { return dataFileInfo{f}, nil }
func (f dataFile) Open() (io.ReadCloser, error) {
	return io.NopCloser(bytes.NewReader(f.data)), nil
}

type dataFileInfo struct {
	f dataFile
}

func (fi dataFileInfo) Name() string       { return path.Base(fi.f.name) }
func (fi dataFileInfo) Size() int64        { return int64(len(fi.f.data)) }
func (fi dataFileInfo) Mode() fs.FileMode  { return 0644 }
func (fi dataFileInfo) ModTime() time.Time { return time.Time{} }
func (fi dataFileInfo) IsDir() bool        { return false }
func (fi dataFileInfo) Sys() interface{}   { return nil }

// hasPathPrefix报告路径s是否以
// 前缀中的元素。
func hasPathPrefix(s, prefix string) bool {
	switch {
	default:
		return false
	case len(s) == len(prefix):
		return s == prefix
	case len(s) > len(prefix):
		if prefix != "" && prefix[len(prefix)-1] == '/' {
			return strings.HasPrefix(s, prefix)
		}
		return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
	}
}
